<!DOCTYPE html>
<!-- Ported from the OpenGL Samples Pack https://github.com/g-truc/ogl-samples/blob/master/tests/gl-320-texture-derivative.cpp and adapted from Mozilla's sample 5 https://github.com/mdn/webgl-examples/tree/gh-pages/tutorial/sample5 -->
<html lang="en">

<head>
    <title>WebGL 2 Samples - texture_derivative</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script src="third-party/gl-matrix-min.js"></script>
</head>

<body>
    <div id="info">WebGL 2 Samples - texture_derivative</div>
    <p id="description">
        This sample demonstrates using dFdx and dFdy (glsl built-in derivative functions in fragment shader) to compute normals.
    </p>

    <!-- WebGL 2 shaders -->
    <script id="vs" type="x-shader/x-vertex">
        #version 300 es
        #define POSITION_LOCATION 0
        #define TEXCOORD_LOCATION 4
        
        precision highp float;
        precision highp int;

        uniform mat4 mvMatrix;
        uniform mat4 pMatrix;

        layout(location = POSITION_LOCATION) in vec3 position;
        layout(location = TEXCOORD_LOCATION) in vec2 texcoord;

        out vec2 v_uv;
        out vec3 v_position;

        void main()
        {
            v_uv = texcoord;
            v_position = vec3(mvMatrix * vec4(position, 1.0));
            gl_Position = pMatrix * mvMatrix * vec4(position, 1.0);
        }
    </script>

    <script id="fs" type="x-shader/x-fragment">
        #version 300 es
        precision highp float;
        precision highp int;
        precision highp sampler2D;

        uniform sampler2D diffuse;

        in vec2 v_uv;
        in vec3 v_position;

        out vec4 color;

        void main()
        {
            color = texture(diffuse, v_uv);

            // Compute flat normal using gradient
            vec3 fdx = vec3(dFdx(v_position.x), dFdx(v_position.y), dFdx(v_position.z));
            vec3 fdy = vec3(dFdy(v_position.x), dFdy(v_position.y), dFdy(v_position.z));

            vec3 N = normalize(cross(fdx, fdy));
            color = mix(color, vec4(N, 1.0), 0.5);
        }

    </script>

    <script src="utility.js"></script>
    <script>
    (function () {
        'use strict';

        var canvas = document.createElement('canvas');
        canvas.width = Math.min(window.innerWidth, window.innerHeight);
        canvas.height = canvas.width;
        document.body.appendChild(canvas);

        var gl = canvas.getContext( 'webgl2', { antialias: false } );
        var isWebGL2 = !!gl;
        if(!isWebGL2) {
            document.getElementById('info').innerHTML = 'WebGL 2 is not available.  See <a href="https://www.khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">How to get a WebGL 2 implementation</a>';
            return;
        }

        // -- Init program
        var program = createProgram(gl, getShaderSource('vs'), getShaderSource('fs'));
        var mvMatrixLocation = gl.getUniformLocation(program, 'mvMatrix');
        var pMatrixLocation = gl.getUniformLocation(program, 'pMatrix');
        var diffuseLocation = gl.getUniformLocation(program, 'diffuse');

        // -- Init buffers

        var positions = new Float32Array([
          // Front face
          -1.0, -1.0,  1.0,
           1.0, -1.0,  1.0,
           1.0,  1.0,  1.0,
          -1.0,  1.0,  1.0,

          // Back face
          -1.0, -1.0, -1.0,
          -1.0,  1.0, -1.0,
           1.0,  1.0, -1.0,
           1.0, -1.0, -1.0,

          // Top face
          -1.0,  1.0, -1.0,
          -1.0,  1.0,  1.0,
           1.0,  1.0,  1.0,
           1.0,  1.0, -1.0,

          // Bottom face
          -1.0, -1.0, -1.0,
           1.0, -1.0, -1.0,
           1.0, -1.0,  1.0,
          -1.0, -1.0,  1.0,

          // Right face
           1.0, -1.0, -1.0,
           1.0,  1.0, -1.0,
           1.0,  1.0,  1.0,
           1.0, -1.0,  1.0,

          // Left face
          -1.0, -1.0, -1.0,
          -1.0, -1.0,  1.0,
          -1.0,  1.0,  1.0,
          -1.0,  1.0, -1.0
        ]);
        var vertexPosBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var texCoords = new Float32Array([
            // Front face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,

            // Back face
            1.0, 1.0,
            1.0, 0.0,
            0.0, 0.0,
            0.0, 1.0,

            // Top face
            1.0, 0.0,
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,

            // Bottom face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,

            // Right face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,

            // Left face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0
        ]);
        var vertexTexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        // Element buffer
        var indexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

        var cubeVertexIndices = [
          0,  1,  2,      0,  2,  3,    // front
          4,  5,  6,      4,  6,  7,    // back
          8,  9,  10,     8,  10, 11,   // top
          12, 13, 14,     12, 14, 15,   // bottom
          16, 17, 18,     16, 18, 19,   // right
          20, 21, 22,     20, 22, 23    // left
        ];

        // Now send the element array to GL

        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);

        // -- Init VertexArray
        var vertexArray = gl.createVertexArray();
        gl.bindVertexArray(vertexArray);

        var vertexPosLocation = 0; // set with GLSL layout qualifier
        gl.enableVertexAttribArray(vertexPosLocation);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.vertexAttribPointer(vertexPosLocation, 3, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var vertexTexLocation = 4; // set with GLSL layout qualifier
        gl.enableVertexAttribArray(vertexTexLocation);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.vertexAttribPointer(vertexTexLocation, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

        gl.bindVertexArray(null);

        // -- Init Texture

        var imageUrl = '../assets/img/Di-3d.png';
        var texture;
        loadImage(imageUrl, function(image) {
            // -- Init 2D Texture
            texture = gl.createTexture();
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

            // -- Allocate storage for the texture
            gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB8, 512, 512);
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGB, gl.UNSIGNED_BYTE, image);

            requestAnimationFrame(render);

        });

        // -- Initialize render variables
        var orientation = [0.0, 0.0, 0.0];

        var modelMatrix = mat4.create();

        var mvMatrix = mat4.create();
        var translate = vec3.create();
        vec3.set(translate, 0, 0, -10);
        mat4.translate(mvMatrix, modelMatrix, translate);
        var perspectiveMatrix = mat4.create();
        mat4.perspective(perspectiveMatrix, 0.785, 1, 1, 1000);

        // -- Mouse Behaviour

        var mouseDown = false;
        var lastMouseX = 0;
        var lastMouseY = 0;

        canvas.onmousedown = function(event) {
            mouseDown = true;
            lastMouseX = event.clientX;
            lastMouseY = event.clientY;
        };

        canvas.onmouseup = function(event) {
            mouseDown = false;
        };

        canvas.onmousemove = function(event) {
            var newX = event.clientX;
            var newY = event.clientY;

            var deltaX = newX - lastMouseX;
            var deltaY = newY - lastMouseY;

            var m = mat4.create();

            mat4.rotateX(m, m, deltaX / 100.0);
            mat4.rotateY(m, m, deltaY / 100.0);

            var scale = vec3.create();
            vec3.set(scale, (1 + deltaX / 1000.0), (1 + deltaX / 1000.0), (1 + deltaX / 1000.0));
            mat4.scale(m, m, scale);

            mat4.multiply(mvMatrix, mvMatrix, m);

            lastMouseX = newX;
            lastMouseY = newY;
        };

        function render() {
            // -- Render
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT);

            gl.enable(gl.DEPTH_TEST);
            gl.enable(gl.CULL_FACE);
            gl.cullFace(gl.BACK);

            orientation[0] = 0.00020; // yaw
            orientation[1] = 0.00010; // pitch
            orientation[2] = 0.00005; // roll

            mat4.rotateX(mvMatrix, mvMatrix, orientation[0] * Math.PI);
            mat4.rotateY(mvMatrix, mvMatrix, orientation[1] * Math.PI);
            mat4.rotateZ(mvMatrix, mvMatrix, orientation[2] * Math.PI);

            gl.bindVertexArray(vertexArray);
            gl.useProgram(program);
            gl.uniformMatrix4fv(mvMatrixLocation, false, mvMatrix);
            gl.uniformMatrix4fv(pMatrixLocation, false, perspectiveMatrix);
            gl.uniform1i(diffuseLocation, 0);

            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture);

            gl.drawElementsInstanced(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0, 1);

            requestAnimationFrame(render);
        }

        // If you have a long-running page, and need to delete WebGL resources, use:
        //
        // gl.deleteBuffer(vertexPosBuffer);
        // gl.deleteBuffer(vertexTexBuffer);
        // gl.deleteBuffer(indexBuffer);
        // gl.deleteTexture(texture);
        // gl.deleteProgram(program);
        // gl.deleteVertexArray(vertexArray);

    })();
    </script>
    <div id="highlightedLines"  style="display: none">#L63-L66</div>

</body>

</html>
